package user_service

import (
	"fmt"
	"math/rand"
	"peilian-api/app/global/common"
	"peilian-api/app/global/databases/tables"
	"peilian-api/app/global/errors"
	"peilian-api/app/global/variable"
	"peilian-api/app/model"
	"peilian-api/utils/sdk"
	"peilian-api/utils/tools"
	"time"

	"github.com/gin-gonic/gin"
)

type PublicParam struct {
	Page     int `json:"page" form:"page,default=1" `
	PageSize int `json:"page_size" form:"page_size,default=10"`
}

type AddManageRequest struct {
	Name         string `json:"name" form:"name" binding:"required"`
	AccountName  string `json:"account_name" form:"account_name" binding:"required"`
	Phone        string `json:"phone" form:"phone"`
	Email        string `json:"email" form:"email" binding:"required"`
	RoleId       uint   `json:"role_id" form:"role_id"`
	ConstituteId uint   `json:"constitute_id" form:"constitute_id"`
	Type         string `json:"type" form:"type"`
}

type UpdateManageRequest struct {
	Id           int    `json:"id" form:"id" uri:"id"`
	Name         string `json:"name" form:"name" binding:"required"`
	AccountName  string `json:"account_name" form:"account_name" binding:"required"`
	Phone        string `json:"phone" form:"phone"`
	Email        string `json:"email" form:"email" binding:"required"`
	RoleId       uint   `json:"role_id" form:"role_id" binding:"required"`
	ConstituteId uint   `json:"constitute_id" form:"constitute_id"`
}

type DeleteManageRequest struct {
	Id int `json:"id" uri:"id" binding:"required"`
}

type GetManageRequest struct {
	Page       int    `json:"page" form:"page,default=1" `
	PageSize   int    `json:"page_size" form:"page_size,default=10"`
	Agent      string `json:"agent" form:"agent"`
	Query      string `json:"query" form:"query"`
	School     string `json:"school" form:"school"`
	Academy    string `json:"academy" form:"academy"`
	Profession string `json:"profession" form:"profession"`
}

// 定义响应数据
type ManageResp struct {
	Id          uint   `json:"id"`
	Name        string `json:"name"`
	AccountName string `json:"account_name"`
	Phone       string `json:"phone"`
	Email       string `json:"email"`
	IsShow      bool   `json:"is_show"`
	AgentName   string `json:"agent_name"`
	RoleName    string `json:"role_name"`
	RoleId      uint   `json:"role_id"`
}

type AdminService struct{}

// 返回值确认
func (a *AdminService) AddManage(ctx *gin.Context, ar AddManageRequest) *errors.ErrRes {
	var user tables.User
	user.Profession = ""
	user.Academy = ""
	uid, exists := ctx.Get("uid")
	if !exists {
		return errors.NewErrInfo(errors.PublicErrorCode, fmt.Errorf("操作失败~"))
	}
	// 1. 判断该用户是否存在
	ues, exists, _ := tables.GetUserInfoByaccountName(ar.AccountName)
	if exists && tools.Contains(tables.AdminArr, string(ues.Type)) {
		return errors.NewErrInfo(errors.PublicErrorCode, fmt.Errorf("该用户名已经被占用，请修改用户名~"))
	}
	// 2. 判断邮箱是否已经存在
	ues, err := tables.GetUserInfoByUserEmail(ar.Email, tables.AdminArr)
	if err != nil {
		return errors.NewErrInfo(errors.PublicErrorCode, fmt.Errorf("获取邮箱失败，请重新再试~"))
	}
	if ues.ID != 0 {
		return errors.NewErrInfo(errors.PublicErrorCode, fmt.Errorf("该邮箱已经被占用,请修改正确邮箱~"))
	}

	us, _ := tables.GetAgentInfoByUid(uid.(uint))
	var school = us.School         // 学校不可能为空
	var level = tables.RolesLevel0 // 普通用户
	var agentId = us.AgentID
	// 3. 如果未选择学院和专业，则根据学校去查询信息 否则，根据constitute_id查询结果
	if ar.ConstituteId == 0 {
		whereMap := make(map[string]interface{}, 0)
		whereMap["name"] = school
		whereMap["type"] = "school"
		cs, _ := new(tables.Constitute).GetInfoByWhere(whereMap)
		ar.ConstituteId = cs.ID
		level = tables.RolesLevel2 // 学校管理员
	} else {
		ci, _ := new(tables.Constitute).GetConstituteById(int(ar.ConstituteId))
		if ci.Type == tables.SchoolType {
			user.School = ci.Name
			level = tables.RolesLevel2 // 学校管理员
			agentId = ci.AgentId
		} else if ci.Type == tables.AcademyType {
			user.Academy = ci.Name
			level = tables.RolesLevel3 // 学院管理员
			ci, _ := new(tables.Constitute).GetConstituteById(int(ci.BelongToId))
			user.School = ci.Name
			agentId = ci.AgentId
		} else {
			user.Profession = ci.Name
			level = tables.RolesLevel4 // 专业管理员
			ci, _ := new(tables.Constitute).GetConstituteById(int(ci.BelongToId))
			user.Academy = ci.Name
			ci, _ = new(tables.Constitute).GetConstituteById(int(ci.BelongToId))
			user.School = ci.Name
			agentId = ci.AgentId
		}
	}

	// 生成密码
	password := fmt.Sprintf("%06v", rand.New(rand.NewSource(time.Now().UnixNano())).Int31n(1000000))

	user.RoleId = ar.RoleId // 角色
	user.Level = level      // 管理员类型
	if level == tables.RolesLevel1 {
		user.Type = tables.UserTypeSadmin // 超级账户sadmin
	} else {
		user.Type = tables.UserTypeAdmin // 统一admin
	}
	user.AgentID = agentId
	user.Name = ar.Name
	user.Phone = ar.Phone
	user.AccountName = &ar.AccountName
	user.Password = tools.Md5DataString(password)
	user.Email = ar.Email
	user.Grade = "" // grade 无
	user.ConstituteID = ar.ConstituteId
	user.CreateUserId = uid.(uint)
	// 添加用户
	rows, err := tables.AddUser(user)
	if err != nil || rows == 0 {
		return errors.NewErrInfo(errors.PublicErrorCode, fmt.Errorf("用户添加失败~"))
	}
	// 发送密码
	if err := sdk.SendMailInfo(sdk.MsgTypePassword, variable.Config.MailInfo.BusinessMail, sdk.MailInfo{RealName: ar.Name, AccountName: ar.AccountName, Password: password, MailTo: ar.Email}); err != nil {
		return errors.NewErrInfo(errors.PublicErrorCode, fmt.Errorf("密码发送失败~"))
	}
	// 记录日志
	go func() {
		defer tools.RecoverGoroutine()
		new(tables.OptionLog).InsertOptionLog(ctx, rows, err)
	}()

	return nil
}

func UpdateManage(ctx *gin.Context, id int, ur UpdateManageRequest) error {
	var whereMap map[string]interface{}
	whereMap = make(map[string]interface{}, 0)
	whereMap["phone"] = ur.Phone
	whereMap["level"] = tables.RolesLevel0
	whereMap["role_id"] = ur.RoleId
	whereMap["constitute_id"] = ur.ConstituteId
	whereMap["name"] = ur.Name
	whereMap["account_name"] = &ur.AccountName
	var school = ""
	var academy = ""
	var profession = ""
	var level = tables.RolesLevel0

	// 如果未选择学院和专业，则根据学校去查询信息 否则，根据constitute_id查询结果
	if ur.ConstituteId == 0 {
		whereMap := make(map[string]interface{}, 0)
		whereMap["name"] = school
		whereMap["type"] = "school"
		cs, _ := new(tables.Constitute).GetInfoByWhere(whereMap)
		level = tables.RolesLevel2 // 学校管理员
		school = cs.Name
	} else {
		ci, _ := new(tables.Constitute).GetConstituteById(int(ur.ConstituteId))
		if ci.Type == tables.SchoolType {
			school = ci.Name
			level = tables.RolesLevel2 // 学校管理员
		} else if ci.Type == tables.AcademyType {
			academy = ci.Name
			level = tables.RolesLevel3 // 学院管理员
			ci, _ := new(tables.Constitute).GetConstituteById(int(ci.BelongToId))
			school = ci.Name
		} else {
			profession = ci.Name
			level = tables.RolesLevel4 // 专业管理员
			ci, _ := new(tables.Constitute).GetConstituteById(int(ci.BelongToId))
			academy = ci.Name
			ci, _ = new(tables.Constitute).GetConstituteById(int(ci.BelongToId))
			school = ci.Name
		}
	}
	// 根据id 更新数据
	whereMap["school"] = school
	whereMap["academy"] = academy
	whereMap["profession"] = profession
	whereMap["level"] = level

	d, err := tables.UpdateMapUserInfoById(whereMap, id)
	// 记录日志
	go func() {
		defer tools.RecoverGoroutine()
		new(tables.OptionLog).InsertOptionLog(ctx, d, err)
	}()
	return err
}

// 删除信息ID
func DeleteByIdManage(ctx *gin.Context, id int) error {
	d, err := tables.DeleteUserById(id)
	// 记录日志
	go func() {
		defer tools.RecoverGoroutine()
		new(tables.OptionLog).InsertOptionLog(ctx, d, err)
	}()
	return err
}

// 获取用户信息
func GetUserInfoManage(ctx *gin.Context, gr GetManageRequest) (count int64, out []ManageResp, err error) {
	// 获取当前用户的用户名
	uid, exists := ctx.Get("uid")
	if !exists {
		return 0, out, fmt.Errorf("操作失败~")
	}
	us, _ := tables.GetAgentInfoByUid(uid.(uint))
	var query = gr.Query
	var school = gr.School
	var page = gr.Page
	var pageSize = gr.PageSize
	var whereMap map[string]interface{}
	var agentId uint = 0
	whereMap = make(map[string]interface{}, 0)
	whereMap["agent_name"] = gr.Agent
	// 根据agent获取agent_id
	agentList, _ := new(tables.Agent).GetAgentByWhere(whereMap, "")
	if len(agentList) != 0 {
		agentId = agentList[0].ID
	} else {
		agentId = us.AgentID
	}

	whereMap = make(map[string]interface{}, 0)
	var whereStr = fmt.Sprintf("id != %d", uid.(uint)) // 排除自身的信息
	if len(gr.School) != 0 {
		whereMap["school"] = school
	}
	if len(gr.Academy) != 0 {
		whereMap["academy"] = gr.Academy
	}
	if len(gr.Profession) != 0 {
		whereMap["profession"] = gr.Profession
	}
	if agentId != 0 {
		whereMap["agent_id"] = agentId
	} else {
		whereMap["agent_id"] = us.AgentID
	}

	if us.Type == tables.UserTypeAdmin { // 如果不是超级账号，查询自身建立的管理员
		// whereMap["create_user_id"] = uid.(uint)
		whereStr = whereStr + " and type = 'admin'" // 排除自身的信息
	} else if us.Type == tables.UserTypeSadmin {
		whereStr = whereStr + " and (type = 'admin' or type = 'sadmin')"
	}
	if len(query) != 0 {
		query = "%" + query + "%"
		whereStr = whereStr + fmt.Sprintf(" and (account_name like '%s' or name like '%s' or phone like '%s')", query, query, query)
	}
	userList, count, err := new(tables.User).GetUserListByWhere(whereMap, whereStr, "created_at desc", page, pageSize)
	if err != nil {
		return 0, out, fmt.Errorf("查询失败~")
	}
	// 整理返回数据
	for i := 0; i < len(userList); i++ {
		var mr ManageResp
		mr.IsShow = false
		mr.Id = userList[i].ID
		mr.Name = userList[i].Name
		mr.AccountName = *userList[i].AccountName
		mr.Phone = userList[i].Phone
		mr.Email = userList[i].Email
		mr.RoleName = userList[i].Roles.RoleName
		mr.AgentName = userList[i].Agent.AgentName
		mr.RoleId = userList[i].Roles.ID
		if userList[i].CreateUserId == uid {
			mr.IsShow = true
		}
		out = append(out, mr)
	}

	return count, out, nil
}

// 找回密码
func AccountRetrieve(ctx *gin.Context, fp model.ForgetPasswordReq) *errors.ErrRes {
	email := fp.Email
	dynamicCode := fp.DynamicCode
	// redis
	sc := variable.DB.Redis.Get(fmt.Sprintf(common.RedisCodeKey, tools.Md5DataString(email)))
	redisValue, _ := sc.Result()
	if sc.Err() != nil || len(redisValue) == 0 {
		return errors.NewErrInfo(errors.PublicErrorCode, fmt.Errorf("验证码失效，请获取新的验证码"))
	}
	if redisValue != dynamicCode {
		return errors.NewErrInfo(errors.PublicErrorCode, fmt.Errorf("验证码不正确，请查看邮箱，获取正确的验证码~"))
	}
	// 根据账号查询账号是否存在
	u, _ := tables.GetUserInfoByUserEmail(email, tables.AdminArr)
	if u.ID == 0 {
		return errors.NewErrInfo(errors.PublicErrorCode, fmt.Errorf("邮箱不存在，请重新输入正确的邮箱~"))
	}
	// 判断同一个邮箱某一个时间段内是否找回过密码
	// redisValue, _ = variable.DB.Redis.Get(fmt.Sprintf(common.RedisAccountCountKey, tools.Md5DataString(email), *u.AccountName)).Result()
	// if len(redisValue) != 0 {
	// 	return errors.NewErrInfo(errors.PublicErrorCode, fmt.Errorf("您7天内找回过密码，请注意查看邮箱~"))
	// }

	// 生成密码
	password := fmt.Sprintf("%06v", rand.New(rand.NewSource(time.Now().UnixNano())).Int31n(1000000))
	// 发送密码
	err := sdk.SendMailInfo(sdk.MsgTypeRepassword, variable.Config.MailInfo.BusinessMail, sdk.MailInfo{MailTo: u.Email, RealName: u.Name, AccountName: *u.AccountName, Password: password})
	if err != nil {
		return errors.NewErrInfo(errors.PublicErrorCode, fmt.Errorf("用户找回密码失败~"))
	}
	// 更新用户密码
	if _, err := tables.UpdateMapUserInfoById(map[string]interface{}{"password": tools.Md5DataString(password)}, int(u.ID)); err != nil {
		return errors.NewErrInfo(errors.PublicErrorCode, fmt.Errorf("用户找回密码失败~"))
	}
	// 用户失效，强制重新登录
	if ic := variable.DB.Redis.Del(fmt.Sprintf(common.RedisAccountKey, *u.AccountName)); ic.Err() != nil {
		return errors.NewErrInfo(errors.PublicErrorCode, fmt.Errorf("用户找回密码失败~"))
	}
	if sc := variable.DB.Redis.Set(fmt.Sprintf(common.RedisAccountCountKey, tools.Md5DataString(email), *u.AccountName), password, 7*24*time.Hour); sc.Err() != nil {
		return errors.NewErrInfo(errors.PublicErrorCode, fmt.Errorf("用户找回密码失败~"))
	}
	// 记录日志
	go func() {
		defer tools.RecoverGoroutine()
		uri := ctx.Request.URL.Path
		var olog = tables.OptionLog{
			TraceId:     "-",
			UserId:      0,
			UserAccount: email,
			UserName:    "-",
			IpAddress:   ctx.ClientIP(),
			AgentName:   email,
			OneModule:   common.UriOptionLogMap[uri][0].(string),
			TwoModule:   common.UriOptionLogMap[uri][1].(string),
			OptionType:  common.UriOptionLogMap[uri][2].(string),
			Number:      1,
			ErrInfo:     "ok",
		}
		variable.DB.MySQL.Model(&tables.OptionLog{}).Save(&olog)
		new(tables.OptionLog).InsertOptionLog(ctx, 1, nil)
	}()
	return nil
}

// 修改密码
func UpdatePassword(ctx *gin.Context, up model.UpdatePasswordReq) *errors.ErrRes {
	// userId := up.UserId
	oldPwd := up.OldPassword
	newPwd := up.NewPassword
	cfmPwd := up.ConfirmPassword

	us := tables.User{}
	uid, exists := ctx.Get("uid")
	if !exists {
		return errors.NewErrInfo(errors.PublicErrorCode, fmt.Errorf("操作失败~"))
	}
	us, _ = tables.GetAgentInfoByUid(uid.(uint))

	// if uid != userId {
	// 	return errors.NewErrInfo(errors.PublicErrorCode, fmt.Errorf("账户名错误，请确认账户后，重新修改密码"))
	// }
	if us.Password != oldPwd {
		return errors.NewErrInfo(errors.PublicErrorCode, fmt.Errorf("输入旧密码错误"))
	}
	if newPwd != cfmPwd {
		return errors.NewErrInfo(errors.PublicErrorCode, fmt.Errorf("新密码与确认密码不一致，请重新输入"))
	}

	// 更新用户密码
	if _, err := tables.UpdateMapUserInfoById(map[string]interface{}{"password": newPwd}, int(uid.(uint))); err != nil {
		return errors.NewErrInfo(errors.PublicErrorCode, fmt.Errorf("用户修改密码失败"))
	}
	// 用户失效，强制重新登录
	if ic := variable.DB.Redis.Del(fmt.Sprintf(common.RedisAccountKey, *us.AccountName)); ic.Err() != nil {
		return errors.NewErrInfo(errors.PublicErrorCode, fmt.Errorf("用户修改密码失败"))
	}
	// 记录日志
	go func() {
		defer tools.RecoverGoroutine()
		new(tables.OptionLog).InsertOptionLog(ctx, 1, nil)
	}()

	return nil
}

func SendMailDynamicCode(ctx *gin.Context, mail string) *errors.ErrRes {
	// 判断改mail是否存在
	var where map[string]interface{} = make(map[string]interface{}, 0)
	where["email"] = mail
	u, err := new(tables.User).GetUsersByWhere(where, "*")
	if err != nil {
		return errors.NewErrInfo(errors.DatabaseMysqlErrorCode, fmt.Errorf(errors.DatabaseMySqlOptionFail))
	}
	if len(u) == 0 {
		return errors.NewErrInfo(errors.DatabaseMysqlErrorCode, fmt.Errorf("用户不存在,请联系管理员开通用户"))
	}
	if len(u[0].Type) == 0 || !tools.Contains(tables.AdminArr, u[0].Type) {
		return errors.NewErrInfo(errors.DatabaseMysqlErrorCode, fmt.Errorf("用户不存在,请联系管理员开通用户"))
	}

	// 判断同一个邮箱某一个时间段内是否找回过密码
	redisValue, _ := variable.DB.Redis.Get(fmt.Sprintf(common.RedisFindKey, tools.Md5DataString(mail))).Result()
	if len(redisValue) != 0 {
		return errors.NewErrInfo(errors.PublicErrorCode, fmt.Errorf("您7天内找回过密码，请注意查看邮箱~"))
	}

	code := fmt.Sprintf("%04v", rand.New(rand.NewSource(time.Now().UnixNano())).Int31n(10000))
	redisCodeKey := fmt.Sprintf(common.RedisCodeKey, tools.Md5DataString(mail))
	sc := variable.DB.Redis.Set(redisCodeKey, code, 10*60*time.Second)
	if sc.Err() != nil {
		return errors.NewErrInfo(errors.PublicErrorCode, fmt.Errorf("验证码生成失败"))
	}
	// 发送验证码
	if err := sdk.SendMailInfo(sdk.MsgTypeCode, variable.Config.MailInfo.BusinessMail, sdk.MailInfo{MailTo: mail, Code: code}); err != nil {
		return errors.NewErrInfo(errors.PublicErrorCode, fmt.Errorf("邮件发送失败"))
	}

	// 设置发送验证码的频次
	if sc := variable.DB.Redis.Set(fmt.Sprintf(common.RedisFindKey, tools.Md5DataString(mail)), code, 7*24*time.Hour); sc.Err() != nil {
		return errors.NewErrInfo(errors.PublicErrorCode, fmt.Errorf("用户找回密码失败~"))
	}
	return nil
}
